package me.zhengjie.modules.security.rest

import cn.hutool.core.util.IdUtil
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.servlet.http.HttpServletRequest
import me.zhengjie.common.annotation.rest.AnonymousPostMapping
import me.zhengjie.common.annotation.rest.AnonymousGetMapping
import me.zhengjie.common.config.RsaProperties
import me.zhengjie.common.exception.BadRequestException
import me.zhengjie.common.utils.RedisUtil
import me.zhengjie.common.utils.SecurityUtils
import me.zhengjie.common.utils.cypto.RSAUtils
import me.zhengjie.logging.annotation.Log
import me.zhengjie.modules.security.config.bean.LoginCodeEnum
import me.zhengjie.modules.security.config.bean.LoginProperties
import me.zhengjie.modules.security.config.bean.SecurityProperties
import me.zhengjie.modules.security.security.TokenProvider
import me.zhengjie.modules.security.service.OnlineUserService
import me.zhengjie.modules.security.service.dto.AuthUserDto
import me.zhengjie.modules.security.service.dto.JwtUserDto
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import java.util.concurrent.TimeUnit

/**
 * @author Kuki Wu
 * @date 2024-11-23
 * 授权、根据token获取用户详细信息
 */
@RestController
@RequestMapping("/auth")
@Schema(description = "系统：系统授权接口")
class AuthenticationController(
    private val properties: SecurityProperties,
    private val redisUtils: RedisUtil,
    private val onlineUserService: OnlineUserService,
    private val tokenProvider: TokenProvider,
    private val authenticationManagerBuilder: AuthenticationManagerBuilder,
    private val loginProperties: LoginProperties,
    private val rsaProperties: RsaProperties
) {

    @Log("用户登录")
    @Schema(description = "登录授权")
    @AnonymousPostMapping(value = ["/login"])
    fun login(@Validated @RequestBody authUser: AuthUserDto, request: HttpServletRequest?): ResponseEntity<*> {
        // 密码解密

        // 密码解密
        val password: String = RSAUtils.decryptByPrivateKey(RsaProperties.privateKey!!, authUser.password!!)!!
        // 查询验证码
        // 查询验证码
        val code = redisUtils[authUser.uuid] as String?
        // 清除验证码
        // 清除验证码
        redisUtils.del(authUser.uuid)
        if (StringUtils.isBlank(code)) {
            throw BadRequestException("验证码不存在或已过期")
        }
        if (StringUtils.isBlank(authUser.code) || !authUser.code.equals(code, ignoreCase = true)) {
            throw BadRequestException("验证码错误")
        }
        val authenticationToken = UsernamePasswordAuthenticationToken(authUser.username, password)
        val authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken)
        SecurityContextHolder.getContext().authentication = authentication
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        val token: String = tokenProvider.createToken(authentication)
        val jwtUserDto = authentication.principal as JwtUserDto
        // 返回 token 与 用户信息
        // 返回 token 与 用户信息
        val authInfo = hashMapOf<String, Any>(
            "token" to (properties.tokenStartWith + token),
            "user" to jwtUserDto
        )

        if (loginProperties.isSingleLogin) {
            // 踢掉之前已经登录的token
            onlineUserService.kickOutForUsername(authUser.username!!)
        }
        // 保存在线信息
        // 保存在线信息
        onlineUserService.save(jwtUserDto!!, token, request)
        // 返回登录信息
        // 返回登录信息
        return ResponseEntity.ok<Any>(authInfo)
    }

    @GetMapping(value = ["/info"])
    @Schema(description = "获取用户信息")
    fun getUserInfo(): ResponseEntity<UserDetails?>? {
        return ResponseEntity.ok(SecurityUtils.userDetails)
    }

    @Schema(description = "获取验证码")
    @AnonymousGetMapping(value = ["/code"])
    open fun getCode(): ResponseEntity<Any?>? {
        // 获取运算的结果
        val captcha = loginProperties.captcha
        val uuid = properties.codeKey + IdUtil.simpleUUID()
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        var captchaValue = captcha.text()
        if (captcha.charType - 1 == LoginCodeEnum.ARITHMETIC.ordinal && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        }
        // 保存
        redisUtils[uuid, captchaValue, loginProperties.loginCode!!.expiration] = TimeUnit.MINUTES
        // 验证码信息
        val imgResult = hashMapOf<String, Any> (
                "img" to captcha.toBase64(),
                "uuid" to  uuid
        )

        return ResponseEntity.ok(imgResult)
    }

    @Schema(description = "退出登录")
    @me.zhengjie.common.annotation.AnonymousAccess
    @DeleteMapping(value = ["/logout"])
    fun logout(request: HttpServletRequest?): ResponseEntity<*> {
        onlineUserService.logout(tokenProvider.getToken(request!!)!!)
        return ResponseEntity<Any?>(HttpStatus.OK)
    }
}